<?php
/**
 * socket 工具类  下例为服务端到客户端  反向同理
 * @author dt
 * @time 2017/8/1
 */
namespace common\utils;
use Yii;

class SocketService
{
    //单例
    private static $instance = null;
    //socket 句柄
    private static $ch = null;
    //默认配置
    private static $defaults = [];
    //配置条件
    private static $options = [];
    //服务器的IP地址
    private static $host = '127.0.0.1';
    //服务器的IP地址 端口
    private static $port  = '8080'; 
    //指定域名
    private static $domain = AF_INET;
    //Socket 的类型
    private static $type  = SOCK_STREAM;
    //服务器请求的队列
    private static $queue  = 3; 
    //服务器请求成功返回信息
    private static $msg  = 'success'; 
    //读取客户端信息数据
    private static $number  = 8192; 

    /**
     * socket constructor.
     * @param $options
     */
    private function __construct($options)
    {
        //默认配置在此设置 可以添加自由配置于 $defaults
        
        //合并配置参数
        self::$options = array_merge(self::$defaults, $options);
        //初始化  
        self::init(); 
    }

    /**
     * 单例实例化
     * @param array $options
     * @return socket|null
     */
    public static function getInstance($options = [])
    {
        if (empty(self::$instance)) {
            self::$instance = new self($options);
        }
        return self::$instance;
    }

    /**
     * 初始化
     */
    private static function init()
    {
        error_reporting(E_ALL);
        set_time_limit(0);
    }

    /**
     * 接受参数  并请求
     * @return bool|null|string
     */
    private static function request($query = [])
    {
        //重置socket 参数
        self::$options = $query;
        self::check();
        return create();
    }

    /**
     * 开启句柄
     */
    private static function create()
    {
        //指定域名 类型
        self::$ch = socket_create(self::$domain, self::$type, 0);
        self::sendError(socket_strerror(self::$ch));
        return bind();
    }

    /**
     * 绑定
     */
    private static function bind()
    {
        //绑定socket句柄
        $result = socket_bind(self::$ch, self::$host, self::$port);
        self::sendError(socket_strerror($result));
        return lisence();
    }

    /**
     * 监听
     */
    private static function lisence()
    {
        //监听socket句柄
        $result = socket_listen(self::$ch, self::$queue);
        self::sendError(socket_strerror($result));
        return accept();
    }

    /**
     * 等待客户端请求
     */
    private static function accept()
    {
        //开始请求
        $result = socket_accept(self::$ch);
        self::sendError(socket_strerror($result));
        lisence($result);
        //读取客户端数据信息
        return read($result);
    }

    /**
     * 向客户端写入信息
     */
    private static function write($data)
    {
        //向客户端写入数据
        socket_write($data, self::$msg, strlen(self::$msg));
    }

    /**
     * 读取客户端信息
     */
    private static function read($data)
    {
        //向客户端写入数据
        return socket_read($data, self::$number);
    }

    /**
     * 判断参数 域名  类型
     * @return bool|null|string
     */
    private static function check()
    {
        if(!empty(self::$options['domain'])){
            self::$domain = self::$options['domain'];
        }
        if(!empty(self::$options['type'])){
            self::$type = self::$options['type'];
        }
        if(!empty(self::$options['host'])){
            self::$host = self::$options['host'];
        }
        if(!empty(self::$options['port'])){
            self::$port = self::$options['port'];
        }
        if(!empty(self::$options['queue'])){
            self::$queue = self::$options['queue'];
        }
        if(!empty(self::$options['msg'])){
            self::$msg = self::$options['msg'];
        }
        if(!empty(self::$options['number'])){
            self::$number = self::$options['number'];
        }
    }

    /**
     * 打印错误
     * @param $errMsg
     * @throws Exception
     */
    private static function sendError($errMsg)
    {
        if($errMsg == FALSE){
            $result = [
                'code' => self::$host.'--'.self::$port, //以请求host 和 port 作为错误码
                'message' => socket_strerror($errMsg)
            ];
            throw new Exception(json_encode($result,JSON_UNESCAPED_UNICODE));
        }else{
            return true;
        }
    }

    /**
     * 关闭句柄
     */
    private static function close()
    {
        if (is_resource(self::$ch)) {
            socket_close(self::$ch);
        }
    }

    /**
     * 防止克隆对象
     */
    private function __clone()
    {
        //防止clone函数克隆对象，破坏单例模式
    }

    /**
     * 析构
     */
    public function __destruct()
    {
        self::close();
    }
}


